﻿using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using AutoMapper;
using BaseData.Domain;
using BaseData.ErrorCode;
using BaseData.Infrastructrue.Interfaces;
using BaseData.ReadApi.Application.Command;
using BaseData.ReadApi.Dto;
using MD5Hash;
using MediatR;
using Microsoft.IdentityModel.Tokens;

namespace BaseData.ReadApi.Application.CommandHandler
{
    /// <summary>
    /// 登录命令处理类
    /// </summary>
    public class LogingCommandHandler : IRequestHandler<LogingCommand, Apiresult<ResUserOutput>>
    {
        private readonly IBasepository<UserInfo> userpository;  // 用户信息仓储接口
        private readonly IBasepository<Role> rolepository;  // 角色仓储接口
        private readonly IBasepository<UserRole> uRpository;  // 用户角色仓储接口
        private readonly IBasepository<Permission> permisspository;  // 权限仓储接口
        private readonly IBasepository<RolePermiss> rolePerpository;  // 角色权限仓储接口
        private readonly IMapper mapper;  // 对象映射器
        private readonly ILogger<LogingCommandHandler> logger;  // 日志记录器
        private readonly IConfiguration configuration;  // 配置接口

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="_userpository">用户信息仓储</param>
        /// <param name="_rolepository">角色仓储</param>
        /// <param name="_URpository">用户角色仓储</param>
        /// <param name="_permisspository">权限仓储</param>
        /// <param name="_rolePerpository">角色权限仓储</param>
        /// <param name="_mapper">对象映射器</param>
        /// <param name="_logger">日志记录器</param>
        /// <param name="_configuration">配置接口</param>
        public LogingCommandHandler(
            IBasepository<UserInfo> _userpository,
            IBasepository<Role> _rolepository,
            IBasepository<UserRole> _URpository,
            IBasepository<Permission> _permisspository,
            IBasepository<RolePermiss> _rolePerpository,
            IMapper _mapper,
            ILogger<LogingCommandHandler> _logger,
            IConfiguration _configuration)
        {
            userpository = _userpository;
            rolepository = _rolepository;
            uRpository = _URpository;
            permisspository = _permisspository;
            rolePerpository = _rolePerpository;
            mapper = _mapper;
            logger = _logger;
            configuration = _configuration;
        }

        /// <summary>
        /// 处理登录请求
        /// </summary>
        /// <param name="request">登录请求</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>登录结果</returns>
        public async Task<Apiresult<ResUserOutput>> Handle(LogingCommand request, CancellationToken cancellationToken)
        {
            // 参数验证
            if (string.IsNullOrWhiteSpace(request.UserName))
            {
                return CreateErrorResponse("用户名不能为空");
            }

            if (string.IsNullOrWhiteSpace(request.Password))
            {
                return CreateErrorResponse("密码不能为空");
            }

            try
            {
                // 查找用户信息
                var user = await userpository.GetModel(x => x.UserName == request.UserName);
                if (user == null)
                {
                    logger.LogWarning("登录失败：用户 {UserName} 不存在", request.UserName);
                    return CreateErrorResponse("用户名或密码错误"); // 避免透露用户是否存在的信息
                }

                // 检查密码是否正确
                if (ValidatePassword(request.Password, user.Password))
                {
                    logger.LogInformation("用户 {UserName} 登录成功", request.UserName);
                    var result = await CreateUserResult(user);
                    return CreateSuccessResponse(result);
                }
                else
                {
                    logger.LogWarning("登录失败：用户 {UserName} 密码错误", request.UserName);
                    return CreateErrorResponse("用户名或密码错误"); // 避免透露具体是密码错误
                }
            }
            catch (Exception ex)
            {
                // 记录详细错误日志
                logger.LogError(ex, "登录处理出错：用户 {UserName}，错误信息：{ErrorMessage}", request.UserName, ex.Message);
                return CreateErrorResponse("登录处理出错，请稍后再试");
            }
        }

        private bool ValidatePassword(string inputPassword, string storedPassword)
        {
            // 使用常量时间比较来防止时序攻击
            return CryptographicOperations.FixedTimeEquals(
                Encoding.UTF8.GetBytes(inputPassword.GetMD5()),
                Encoding.UTF8.GetBytes(storedPassword)
            );
        }

        private async Task<ResUserOutput> CreateUserResult(UserInfo user)
        {
            var result = mapper.Map<ResUserOutput>(user);

            // 先获取用户角色
            var userRole = await uRpository.GetModel(x => x.UserId == result.UserId);
            if (userRole == null)
            {
                return result; // 如果没有角色信息，直接返回基础用户信息
            }

            // 获取角色信息
            var role = await rolepository.GetModel(x => x.RoleId == userRole.RoleId);
            if (role != null)
            {
                result.RoleId = role.RoleId;
                result.RoleName = role.RoleName ?? "";

                // 获取角色权限关联
                var rolePermiss = await rolePerpository.GetModel(x => x.RoleId == role.RoleId);
                if (rolePermiss != null)
                {
                    // 获取权限信息
                    var permiss = await permisspository.GetModel(x => x.PermissionId == rolePermiss.PermissionId);
                    if (permiss != null)
                    {
                        result.PermissionName = permiss.PermissionName ?? "";
                    }
                }
            }

            return result;
        }

        private Apiresult<ResUserOutput> CreateSuccessResponse(ResUserOutput result)
        {
            var (accessToken, refreshToken, expiryTime) = CreateTokens(result.UserId, result.UserName, result.RoleId, result.RoleName);
            
            return new Apiresult<ResUserOutput>
            {
                Code = ResultCode.查询成功,
                Message = "登录成功",
                Result = result,
                Token = accessToken,
                RefreshToken = refreshToken,
                TokenExpiry = expiryTime
            };
        }

        /// <summary>
        /// 生成双Token（Access Token + Refresh Token）
        /// </summary>
        /// <param name="UserId">用户ID</param>
        /// <param name="UserName">用户名</param>
        /// <param name="RoleId">角色ID</param>
        /// <param name="RoleName">角色名称</param>
        /// <returns>Tuple包含Access Token, Refresh Token和过期时间</returns>
        private (string accessToken, string refreshToken, DateTime expiryTime) CreateTokens(int UserId, string UserName, int RoleId, string RoleName)
        {
            // 从配置中获取JWT设置
            var jwtSettings = configuration.GetSection("Jwt");
            var secretByte = Encoding.UTF8.GetBytes(jwtSettings["SecretKey"]!);

            // 非对称加密
            var signingKey = new SymmetricSecurityKey(secretByte);

            // 使用256生成数字签名
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

            // 设置声明信息（用户ID、用户名、角色ID、角色名称）
            var claims = new Claim[]
            {
                new Claim("UserId", UserId.ToString()),
                new Claim("UserName", UserName),
                new Claim("RoleId", RoleId.ToString()),
                new Claim("RoleName", RoleName)
            };

            // 生成Access Token（短期有效）
            var accessTokenExpiry = DateTime.Now.AddMinutes(int.Parse(jwtSettings["AccessTokenExpiryMinutes"]!));
            var accessToken = new JwtSecurityToken(
                issuer: jwtSettings["Issuer"],  // 发行人
                audience: jwtSettings["Audience"],  // 受众
                expires: accessTokenExpiry, // 从配置中获取过期时间
                signingCredentials: signingCredentials, // 签名凭证
                claims: claims  // 声明
            );

            // 生成Refresh Token（长期有效）
            var refreshTokenExpiry = DateTime.Now.AddDays(int.Parse(jwtSettings["RefreshTokenExpiryDays"]!));
            var refreshToken = new JwtSecurityToken(
                issuer: jwtSettings["Issuer"],  // 发行人
                audience: jwtSettings["Audience"],  // 受众
                expires: refreshTokenExpiry, // 从配置中获取过期时间
                signingCredentials: signingCredentials, // 签名凭证
                claims: new Claim[]
                {
                    new Claim("UserId", UserId.ToString()),
                    new Claim("TokenType", "RefreshToken")  // 标记为Refresh Token
                }
            );

            // 生成Token字符串
            var jwtHandler = new JwtSecurityTokenHandler();
            var accessTokenString = jwtHandler.WriteToken(accessToken);
            var refreshTokenString = jwtHandler.WriteToken(refreshToken);

            return (accessTokenString, refreshTokenString, accessTokenExpiry);
        }

        private Apiresult<ResUserOutput> CreateErrorResponse(string message)
        {
            return new Apiresult<ResUserOutput>
            {
                Code = ResultCode.查询失败,
                Message = message
            };
        }
    }
}

